using System;
using System.Collections.Generic;

namespace Tycho.Parser.Peg {
    public class NonTerminal : Production {
        public IAssociationSwapper AssociationSwapper;
        public IRule Rule { get; set; }
        public ICompositeTermBuilder TermBuilder;
        private string _name;
        public ICompositeTermFactory CompositeTermFactory;
        public string FirstCaptureName;
        public string LastCaptureName;
        public IPrecedence Precedence;

        public NonTerminal (string name, IRule rule)
            : this (name, rule, new CompositeTermBuilder (null), new IdentityAssociationSwapper ()) {
        }

        public NonTerminal (string name, IRule rule, IEnumerable<string> multiples)
            : this (name, rule, new CompositeTermBuilder (multiples), new IdentityAssociationSwapper ()) {
        }

        public NonTerminal (string name, IRule rule, ICompositeTermBuilder termBuilder, IAssociationSwapper associationSwapper) {
            Name = name;
            Rule = rule;
            TermBuilder = termBuilder;

            AssociationSwapper = associationSwapper;
            CompositeTermFactory = new CompositeTermFactory ();
        }

        public NonTerminal () {
            AssociationSwapper = new IdentityAssociationSwapper ();
            TermBuilder = new CompositeTermBuilder (null);
            CompositeTermFactory = new CompositeTermFactory ();
        }

        public override string Name {
            get {
                return _name;
            }
            set {
                _name = value;
                ErrorInformation = new ProductionErrorInformation (_name);
            }
        }

        public override bool IsInfix {
            get {
                return Rule.IsInfix;
            }
        }

        public override bool IsLeftRecursive {
            get {
                return false;
            }
        }

        public override IInfixInformation InfixInformation
        {
            get
            {
                return Rule.InfixInformation;
            }
        }

        public IEnumerable<string> Multiples {
            get { return TermBuilder.Multiples; }
            set { TermBuilder.Multiples = value; }
        }

        protected override Yield ReallyParse (char[] source, int index, ParseContext context, string sourceString, ParseEnvironment parseEnvironment, Func<ParseResult, Yield> continuation) {
            return Rule.Parse (source, index, context, sourceString, parseEnvironment, parseResult =>
            {
                if (parseResult != null)
                {
                    SourceInformation sinfo = parseEnvironment.SourceFileInformation.CreateSourceInformation(index, parseResult.Index - index);

                    var term = CreateCompositeTerm(sinfo);

                    TermBuilder.Build(term, parseResult.Captures);
                    CompositeTerm resultTerm = AssociationSwapper.Swap(term);

                    return () => continuation(new ParseResult(parseResult.Index, resultTerm, parseResult.Context));
                }
                else
                {
                    return () => continuation(null);
                }
            });
        }

        CompositeTerm CreateCompositeTerm (SourceInformation sinfo) {
            if (Precedence == null) {
                return CompositeTermFactory.CreateCompositeTerm (Name, sinfo);
            } else {
                return CompositeTermFactory.CreateInfixCompositeTerm (Name, Precedence, FirstCaptureName, LastCaptureName, sinfo);
            }
        }
    }
}